/**
 * Copyright (C) 2013-2014 EaseMob Technologies. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hengtiansoft.lfy.chart.applib.controller;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.R.bool;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import android.widget.Toast;


import com.hengtiansoft.lfy.chart.applib.model.DefaultHXSDKModel;
import com.hengtiansoft.lfy.chart.applib.model.HXNotifier;
import com.hengtiansoft.lfy.chart.applib.model.HXNotifier.HXNotificationInfoProvider;
import com.hengtiansoft.lfy.chart.applib.model.HXSDKModel;
import com.hengtiansoft.lfy.chart.DemoHXSDKModel;
import com.hyphenate.EMCallBack;
import com.hyphenate.EMConnectionListener;
import com.hyphenate.EMError;
import com.hyphenate.EMValueCallBack;
import com.hyphenate.chat.EMChatManager;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMContactManager;
import com.hyphenate.chat.EMOptions;
import com.hyphenate.exceptions.HyphenateException;


/**
 * The developer can derive from this class to talk with HuanXin SDK All the
 * Huan Xin related initialization and global listener are implemented in this
 * class which will help developer to speed up the SDK integration。 this is a
 * global instance class which can be obtained in any codes through
 * getInstance()
 * 
 * 开发人员可以选择继承这个环信SDK帮助类去加快初始化集成速度。此类会初始化环信SDK，并设置初始化参数和初始化相应的监听器
 * 不过继承类需要根据要求求提供相应的函数，尤其是提供一个{@link HXSDKModel}. 所以请实现abstract protected
 * HXSDKModel createModel(); 全局仅有一个此类的实例存在，所以可以在任意地方通过getInstance()函数获取此全局实例
 * 
 * @author easemob
 *
 */
public abstract class HXSDKHelper {

    /**
     * 群组更新完成
     */
    static public interface HXSyncListener {
        public void onSyncSucess(boolean success);
    }

    private static final String TAG = "HXSDKHelper";

    /**
     * application context
     */
    protected Context appContext = null;

    /**
     * HuanXin mode helper, which will manage the user data and user preferences
     */
    protected HXSDKModel hxModel = null;

    /**
     * MyConnectionListener
     */
    protected EMConnectionListener connectionListener = null;

    /**
     * HuanXin ID in cache
     */
    protected String hxId = null;

    /**
     * password in cache
     */
    protected String password = null;

    /**
     * init flag: test if the sdk has been inited before, we don't need to init
     * again
     */
    private boolean sdkInited = false;

    /**
     * the global HXSDKHelper instance
     */
    private static HXSDKHelper me = null;

    /**
     * the notifier
     */
    protected HXNotifier notifier = null;

    /**
     * HuanXin sync groups status listener
     */
    private List<HXSyncListener> syncGroupsListeners;

    /**
     * HuanXin sync contacts status listener
     */
    private List<HXSyncListener> syncContactsListeners;

    /**
     * HuanXin sync blacklist status listener
     */
    private List<HXSyncListener> syncBlackListListeners;

    private boolean isSyncingGroupsWithServer = false;

    private boolean isSyncingContactsWithServer = false;

    private boolean isSyncingBlackListWithServer = false;

    private boolean isGroupsSyncedWithServer = false;

    private boolean isContactsSyncedWithServer = false;

    private boolean isBlackListSyncedWithServer = false;

    private boolean alreadyNotified = false;

    public boolean isVoiceCalling;
    public boolean isVideoCalling;

    protected HXSDKHelper() {
        me = this;
    }

    /**
     * this function will initialize the HuanXin SDK
     * 
     * @return boolean true if caller can continue to call HuanXin related APIs
     *         after calling onInit, otherwise false.
     * 
     *         环信初始化SDK帮助函数
     *         返回true如果正确初始化，否则false，如果返回为false，请在后续的调用中不要调用任何和环信相关的代码
     * 
     *         for example: 例子：
     * 
     *         public class DemoHXSDKHelper extends HXSDKHelper
     * 
     *         HXHelper = new DemoHXSDKHelper(); if(HXHelper.onInit(context)){
     *         // do HuanXin related work }
     */
    public synchronized boolean onInit(Context context) {
        if (sdkInited) {
            return true;
        }

        appContext = context;

        // create HX SDK model
        hxModel = createModel();

        // create a defalut HX SDK model in case subclass did not provide the
        // model
        if (hxModel == null) {
            hxModel = new DefaultHXSDKModel(appContext);
        }

        int pid = android.os.Process.myPid();
        String processAppName = getAppName(pid);

        Log.d(TAG, "process app name : " + processAppName);

        // 如果app启用了远程的service，此application:onCreate会被调用2次
        // 为了防止环信SDK被初始化2次，加此判断会保证SDK被初始化1次
        // 默认的app会在以包名为默认的process name下运行，如果查到的process name不是app的process
        // name就立即返回
        if (processAppName == null
                || !processAppName
                        .equalsIgnoreCase(hxModel.getAppProcessName())) {
            Log.e(TAG, "enter the service process!");

            // 则此application::onCreate 是被service 调用的，直接返回
            return false;
        }
        EMOptions options = new EMOptions();
        // 默认添加好友时，是不需要验证的，改成需要验证
        options.setAcceptInvitationAlways(hxModel.getAcceptInvitationAlways());
        // 设置是否需要已读回执
        options.setRequireAck(hxModel.getRequireReadAck());
        // 设置是否需要已送达回执
        options.setRequireDeliveryAck(hxModel.getRequireDeliveryAck());
        options.allowChatroomOwnerLeave(
                ((DemoHXSDKModel)hxModel).isChatroomOwnerLeaveAllowed());
        // 初始化环信SDK,一定要先调用init()
        EMClient.getInstance().init(context,options);

      /*  // 设置sandbox测试环境
        if (hxModel.isSandboxMode()) {   //
            EMClient.getInstance().setEnv(EMEnvMode.EMSandboxMode);
        }*/

//         if (hxModel.isDebugMode()) {
//         // set debug mode in development process
//         EMChat.getInstance().setDebugMode(true);
//         }
       // EMClient.getInstance().setDebugMode(true);
        Log.d(TAG, "initialize EMChat SDK");

        initHXOptions();
        initListener();

        syncGroupsListeners = new ArrayList<HXSyncListener>();
        syncContactsListeners = new ArrayList<HXSyncListener>();
        syncBlackListListeners = new ArrayList<HXSyncListener>();

        isGroupsSyncedWithServer = hxModel.isGroupsSynced();
        isContactsSyncedWithServer = hxModel.isContactSynced();
        isBlackListSyncedWithServer = hxModel.isBacklistSynced();

        sdkInited = true;
        return true;
    }

    /**
     * get global instance
     * 
     * @return
     */
    public static HXSDKHelper getInstance() {
        return me;
    }

    public Context getAppContext() {
        return appContext;
    }

    public HXSDKModel getModel() {
        return hxModel;
    }

    public String getHXId() {
        if (hxId == null) {
            hxId = hxModel.getHXId();
        }
        return hxId;
    }

    public String getPassword() {
        if (password == null) {
            password = hxModel.getPwd();
        }
        return password;
    }

    public void setHXId(String hxId) {
        if (hxId != null) {
            if (hxModel.saveHXId(hxId)) {
                this.hxId = hxId;
            }
        }
    }

    public void setPassword(String password) {
        if (hxModel.savePassword(password)) {
            this.password = password;
        }
    }

    /**
     * the subclass must override this class to provide its own model or
     * directly use {@link DefaultHXSDKModel}
     * 
     * @return
     */
    abstract protected HXSDKModel createModel();

    /**
     * please make sure you have to get EMChatOptions by following method and
     * set related options EMChatOptions options =
     * EMChatManager.getInstance().getChatOptions();
     */
    protected void initHXOptions() {
        Log.d(TAG, "init HuanXin Options");
        notifier = createNotifier();
        notifier.init(appContext);

        notifier.setNotificationInfoProvider(getNotificationListener());//调用的是DemoHXSDKHelper的getNotificationListener
    }

    /**
     * subclass can override this api to return the customer notifier
     * 
     * @return
     */
    protected HXNotifier createNotifier() {
        return new HXNotifier();
    }

    public HXNotifier getNotifier() {
        return notifier;
    }

    /**
     * logout HuanXin SDK
     */
    public void logout(final boolean unbindDeviceToken,
            final EMCallBack callback) {
        setPassword(null);
        reset();
        EMClient.getInstance().logout(unbindDeviceToken, new EMCallBack() {

            @Override
            public void onSuccess() {
                if (callback != null) {
                    callback.onSuccess();
                }
            }

            @Override
            public void onError(int code, String message) {
                if (callback != null) {
                    callback.onError(code, message);
                }
            }

            @Override
            public void onProgress(int progress, String status) {
                if (callback != null) {
                    callback.onProgress(progress, status);
                }
            }

        });
    }

    /**
     * 检查是否已经登录过
     * 
     * @return
     */
    public boolean isLogined() {
        return EMClient.getInstance().isLoggedInBefore();
    }

    protected HXNotificationInfoProvider getNotificationListener() {
        return null;
    }

    /**
     * init HuanXin listeners
     */
    protected void initListener() {
        Log.d(TAG, "init listener");

        // create the global connection listener
        connectionListener = new EMConnectionListener() {
            @Override
            public void onDisconnected(int error) {
                if (error == EMError.USER_REMOVED) { // 显示帐号已经被移除
                    onCurrentAccountRemoved();
                } else if (error == EMError.USER_LOGIN_ANOTHER_DEVICE) { //显示帐号在其他设备登陆
                    onConnectionConflict();
                } else {
                    onConnectionDisconnected(error); //连接失败
                }
            }

            @Override
            public void onConnected() {
                //已连接到服务器
                onConnectionConnected();
            }
        };

        // 注册连接监听 注册一个监听连接状态的listener
        EMClient.getInstance().addConnectionListener(connectionListener);
    }

    /**
     * the developer can override this function to handle connection conflict
     * error
     */
    protected void onConnectionConflict() {
    }

    /**
     * the developer can override this function to handle user is removed error
     */
    protected void onCurrentAccountRemoved() {
    }

    /**
     * handle the connection connected
     */
    protected void onConnectionConnected() {
    }

    /**
     * handle the connection disconnect
     * 
     * @param error
     *            see {@link EMError}
     */
    protected void onConnectionDisconnected(int error) {
    }

    /**
     * check the application process name if process name is not qualified, then
     * we think it is a service process and we will not init SDK
     * 
     * @param pID
     * @return
     */
    private String getAppName(int pID) {
        String processName = null;
        ActivityManager am = (ActivityManager) appContext
                .getSystemService(Context.ACTIVITY_SERVICE);
        List l = am.getRunningAppProcesses();
        Iterator i = l.iterator();
        PackageManager pm = appContext.getPackageManager();
        while (i.hasNext()) {
            ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i
                    .next());
            try {
                if (info.pid == pID) {
                    CharSequence c = pm.getApplicationLabel(pm
                            .getApplicationInfo(info.processName,
                                    PackageManager.GET_META_DATA));
                    // Log.d("Process", "Id: "+ info.pid +" ProcessName: "+
                    // info.processName +"  Label: "+c.toString());
                    // processName = c.toString();
                    processName = info.processName;
                    return processName;
                }
            } catch (Exception e) {
                // Log.d("Process", "Error>> :"+ e.toString());
            }
        }
        return processName;
    }

    public void addSyncGroupListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (!syncGroupsListeners.contains(listener)) {
            syncGroupsListeners.add(listener);
        }
    }

    public void removeSyncGroupListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (syncGroupsListeners.contains(listener)) {
            syncGroupsListeners.remove(listener);
        }
    }

    public void addSyncContactListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (!syncContactsListeners.contains(listener)) {
            syncContactsListeners.add(listener);
        }
    }

    public void removeSyncContactListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (syncContactsListeners.contains(listener)) {
            syncContactsListeners.remove(listener);
        }
    }

    public void addSyncBlackListListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (!syncBlackListListeners.contains(listener)) {
            syncBlackListListeners.add(listener);
        }
    }

    public void removeSyncBlackListListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (syncBlackListListeners.contains(listener)) {
            syncBlackListListeners.remove(listener);
        }
    }

    /**
     * 同步操作，从服务器获取群组列表 该方法会记录更新状态，可以通过isSyncingGroupsFromServer获取是否正在更新
     * 和HXPreferenceUtils.getInstance().getSettingSyncGroupsFinished()获取是否更新已经完成
     * 
     * @throws
     */
    public synchronized void asyncFetchGroupsFromServer(
            final EMCallBack callback) {
        if (isSyncingGroupsWithServer) {
            return;
        }

        isSyncingGroupsWithServer = true;

        new Thread() {
            @Override
            public void run() {
                try {
                    EMClient.getInstance().groupManager().getJoinedGroupsFromServer();

                    // in case that logout already before server returns, we
                    // should return immediately
                    if (!EMClient.getInstance().isLoggedInBefore()) {
                        return;
                    }

                    hxModel.setGroupsSynced(true);

                    isGroupsSyncedWithServer = true;
                    isSyncingGroupsWithServer = false;
                    if (callback != null) {
                        callback.onSuccess();
                    }
                } catch (HyphenateException e) {
                    hxModel.setGroupsSynced(false);
                    isGroupsSyncedWithServer = false;
                    isSyncingGroupsWithServer = false;
                    if (callback != null) {
                        callback.onError(e.getErrorCode(), e.toString());
                    }
                }

            }
        }.start();
    }

    public void noitifyGroupSyncListeners(boolean success) {
        for (HXSyncListener listener : syncGroupsListeners) {
            listener.onSyncSucess(success);
        }
    }

    public void asyncFetchContactsFromServer(
            final EMValueCallBack<List<String>> callback) {
        if (isSyncingContactsWithServer) {
            return;
        }

        isSyncingContactsWithServer = true;

        new Thread() {
            @Override
            public void run() {
                List<String> usernames = null;
                try {
                    usernames = EMClient.getInstance().contactManager().getAllContactsFromServer();

                    // in case that logout already before server returns, we
                    // should return immediately
                    if (!EMClient.getInstance().isLoggedInBefore()) {
                        return;
                    }

                    hxModel.setContactSynced(true);

                    isContactsSyncedWithServer = true;
                    isSyncingContactsWithServer = false;
                    if (callback != null) {
                        callback.onSuccess(usernames);
                    }
                } catch (HyphenateException e) {
                    hxModel.setContactSynced(false);
                    isContactsSyncedWithServer = false;
                    isSyncingContactsWithServer = false;
                    e.printStackTrace();
                    if (callback != null) {
                        callback.onError(e.getErrorCode(), e.toString());
                    }
                }

            }
        }.start();
    }

    public void notifyContactsSyncListener(boolean success) {
        for (HXSyncListener listener : syncContactsListeners) {
            listener.onSyncSucess(success);
        }
    }

    public void asyncFetchBlackListFromServer(
            final EMValueCallBack<List<String>> callback) {

        if (isSyncingBlackListWithServer) {
            return;
        }

        isSyncingBlackListWithServer = true;

        new Thread() {
            @Override
            public void run() {
                try {
                    List<String> usernames = null;
                    usernames = EMClient.getInstance().contactManager().getBlackListFromServer();

                    // in case that logout already before server returns, we
                    // should return immediately
                    if (!EMClient.getInstance().isLoggedInBefore()) {
                        return;
                    }

                    hxModel.setBlacklistSynced(true);

                    isBlackListSyncedWithServer = true;
                    isSyncingBlackListWithServer = false;
                    if (callback != null) {
                        callback.onSuccess(usernames);
                    }
                } catch (HyphenateException e) {
                    hxModel.setBlacklistSynced(false);

                    isBlackListSyncedWithServer = false;
                    isSyncingBlackListWithServer = true;
                    e.printStackTrace();

                    if (callback != null) {
                        callback.onError(e.getErrorCode(), e.toString());
                    }
                }

            }
        }.start();
    }

    public void notifyBlackListSyncListener(boolean success) {
        for (HXSyncListener listener : syncBlackListListeners) {
            listener.onSyncSucess(success);
        }
    }

    public boolean isSyncingGroupsWithServer() {
        return isSyncingGroupsWithServer;
    }

    public boolean isSyncingContactsWithServer() {
        return isSyncingContactsWithServer;
    }

    public boolean isSyncingBlackListWithServer() {
        return isSyncingBlackListWithServer;
    }

    public boolean isGroupsSyncedWithServer() {
        return isGroupsSyncedWithServer;
    }

    public boolean isContactsSyncedWithServer() {
        return isContactsSyncedWithServer;
    }

    public void setisContactsSyncedWithServer() {
        isContactsSyncedWithServer = true;
    }

    public boolean isBlackListSyncedWithServer() {
        return isBlackListSyncedWithServer;
    }

    public synchronized void notifyForRecevingEvents() {
        if (alreadyNotified) {
            return;
        }

        // 通知sdk，UI 已经初始化完毕，注册了相应的receiver和listener, 可以接受broadcast了
        //EMClient.getInstance().setAppInited();
        alreadyNotified = true;
    }

    synchronized void reset() {
        isSyncingGroupsWithServer = false;
        isSyncingContactsWithServer = false;
        isSyncingBlackListWithServer = false;

        hxModel.setGroupsSynced(false);
        hxModel.setContactSynced(false);
        hxModel.setBlacklistSynced(false);

        isGroupsSyncedWithServer = false;
        isContactsSyncedWithServer = false;
        isBlackListSyncedWithServer = false;

        alreadyNotified = false;
    }
}
